package com.ndp.fb.walle.business.impl;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ndp.fb.model.rdb.AudienceManager;
import com.ndp.fb.model.report.param.EcReportForABTestInputParam;
import com.ndp.fb.mongo.api.EcReportMongoService;
import com.ndp.fb.mongo.api.ProductSetMongoService;
import com.ndp.fb.rdb.api.AudienceManagerRdbService;
import com.ndp.fb.rdb.api.EcTemplateMaterielRdbService;
import com.ndp.fb.rdb.model.EcTemplateMateriel;
import com.ndp.fb.util.DateUtil;
import com.ndp.fb.walle.business.EcReportService;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by infi.he on 2016/3/17.
 */
@Component
public class EcReportServiceImpl implements EcReportService {
    private static Logger logger = LoggerFactory.getLogger(EcReportServiceImpl.class);

    private final static List<String> ID_TO_NAME_LIST = new ArrayList() {
        {
            add("productSet");
            add("audienceTemplate");
            add("creativeTemplate");
            add("bid");
        }
    };

    private final static List<String> ALL_FIELD = new ArrayList() {
        {
            add("impressions");
            add("clicks");
            add("cart");
            add("purchase");
            add("ctr");
            add("costPerMile");
            add("costPerClick");
            add("spend");
            add("budget");
            add("cartR");
            add("costPerCart");
            add("purchaseR");
            add("salesAmount");
            add("roi");
            add("costPerPurchase");
            add("incomePerPurchase");
            add("frequency");
        }
    };

    @Reference
    private EcReportMongoService ecReportMongoService;
    @Reference
    private ProductSetMongoService productSetMongoService;
    @Reference
    private AudienceManagerRdbService audienceManagerRdbService;
    @Reference
    private EcTemplateMaterielRdbService ecTemplateMaterielRdbService;

    @Override
    public Map<String, Object> report(EcReportForABTestInputParam ecReportForABTestInputParam) {
        logger.info("report service start>>>");
        // 1.将需要过滤的名称转换为id
        convertFilterNameToId(ecReportForABTestInputParam);

        // 2.从Mongo中查询数据并将查询出来的id转换为名称
        Map<String, Object> fbDataFromMongoForABTest = queryFromMongo(ecReportForABTestInputParam);
        return fbDataFromMongoForABTest;
    }

    @Override
    public Map<String, Object> abGraph(EcReportForABTestInputParam ecReportForABTestInputParam) {
        logger.info("abGraph service start>>>");
        // 1.将需要过滤的名称转换为id
        convertFilterNameToId(ecReportForABTestInputParam);

        // 2.图形查询时有些参数是固定的、
        ecReportForABTestInputParam.setTimeIncrement(1);
        ecReportForABTestInputParam.setCPage(1);
        ecReportForABTestInputParam.setPSize(10000);

        // 3.从Mongo中查询数据并将查询出来的id转换为名称
        Map<String, Object> fbDataFromMongoForABTest = queryFromMongo(ecReportForABTestInputParam);
        if (CollectionUtils.isEmpty(fbDataFromMongoForABTest)) {
            return null;
        }

        // 4.转换为图形需要的数据
        return convertListDataToGraphData(ecReportForABTestInputParam, fbDataFromMongoForABTest);
    }

    /**
     * 将查询到的List数据转换为图形显示需要的格式
     *
     * @param ecReportForABTestInputParam 查询参数
     * @param fbDataFromMongoForABTest    Mongo查询结果
     * @return 最终转换后的结果
     */
    private Map<String, Object> convertListDataToGraphData(EcReportForABTestInputParam ecReportForABTestInputParam, Map<String, Object> fbDataFromMongoForABTest) {
        String group = ecReportForABTestInputParam.getGroup();
        String since = ecReportForABTestInputParam.getDay().getSince();
        String until = ecReportForABTestInputParam.getDay().getUntil();
        JSONArray listData = JSON.parseArray(JSON.toJSONString(fbDataFromMongoForABTest.get("list")));
        if (null == listData) {
            return null;
        }
        // 将数据转换为以GroupName分组的数据
        Map<Object, List<Object>> mapByGroupName = listData.stream().collect(Collectors.groupingBy(object -> ((JSONObject) object).get(group)));

        JSONObject summaryData = JSON.parseObject(JSON.toJSONString(fbDataFromMongoForABTest.get("summary")));
        if (null == summaryData) {
            return null;
        }
        JSONArray groupNames = summaryData.getJSONArray(group);
        Map<String, List> mapByIndicator = new HashMap<>();
        Map<String, List> mapByDimension = new HashMap<>();
        List<Map<String, Object>> radarData = new ArrayList<>();
        // 初始化返回数据，保证个数和天数差是一致的
        groupNames.forEach(groupName -> {
            List<Object> objects = mapByGroupName.get(groupName);
            List<Map<String, Object>> listForDimension = new ArrayList<>();
            ALL_FIELD.forEach(field -> {
                // 第一种类型数据，即根据指标分组
                Map<String, Object> mapForIndicator = new HashMap<>();
                mapForIndicator.put("name", groupName);

                // 第二种类型数据，即根据维度分组
                Map<String, Object> mapForDimension = new HashMap<>();
                mapForDimension.put("name", field);

                // 计算day，筛选Mongo中符合day的数据进行填充，如果没有找到则赋默认值0
                long dayDiff = DateUtil.dayDiff(DateUtil.stringtoDate(since, "yyyy-MM-dd"), DateUtil.stringtoDate(until, "yyyy-MM-dd")) + 1;
                List<Object> dataList = new ArrayList<>();
                for (int i = 0; i < dayDiff; i++) {
                    String offsetDay = DateUtil.dateToString(DateUtils.addDays(DateUtil.stringtoDate(since, "yyyy-MM-dd"), i), "yyyy-MM-dd");
                    Object[] filteredData = objects.stream().filter(object -> ((JSONObject) object).get("day").equals(offsetDay)).toArray();
                    Object data = 0.0;
                    if (null != filteredData && 0 != filteredData.length) {
                        data = ((JSONObject) filteredData[0]).get(field);
                    }
                    dataList.add(data);
                }
                mapForIndicator.put("data", dataList);
                mapForDimension.put("data", dataList);

                // 第一种类型数据，即根据指标分组
                List listForIndicator = mapByIndicator.get(field);
                if (null == listForIndicator) {
                    listForIndicator = new ArrayList();
                }
                listForIndicator.add(mapForIndicator);
                mapByIndicator.put(field, listForIndicator);

                // 第二种类型数据，即根据维度分组
                listForDimension.add(mapForDimension);
            });
            mapByDimension.put(groupName.toString(), listForDimension);

            Map<String, Object> mapForRadarData = new HashMap<>();
            mapForRadarData.put("name", groupName);

            long sumSpend = objects.parallelStream().mapToLong(object -> ((JSONObject) object).getLong("spend")).sum();
            long sumImpressions = objects.parallelStream().mapToLong(object -> ((JSONObject) object).getLong("impressions")).sum();
            long sumClicks = objects.parallelStream().mapToLong(object -> ((JSONObject) object).getLong("clicks")).sum();
            long sumCart = objects.parallelStream().mapToLong(object -> ((JSONObject) object).getLong("cart")).sum();
            long sumPurchase = objects.parallelStream().mapToLong(object -> ((JSONObject) object).getLong("purchase")).sum();
            long sumSalesAmount = objects.parallelStream().mapToLong(object -> ((JSONObject) object).getLong("salesAmount")).sum();

            Double costPerClick = sumClicks == 0 ? sumSpend : sumSpend * 1.0 / sumClicks;
            Double costPerCart = sumCart == 0 ? sumSpend : sumSpend * 1.0 / sumCart;
            Double costPerPurchase = sumPurchase == 0 ? sumSpend : sumSpend * 1.0 / sumPurchase;
            Double incomePerPurchase = sumPurchase == 0 ? sumSalesAmount : sumSalesAmount * 1.0 / sumPurchase;
            Double costPerMile = sumImpressions == 0 ? sumSpend : sumSpend * 1000.0 / sumImpressions;
            Double roi = sumSpend == 0 ? sumSalesAmount : sumSalesAmount * 1.0 / sumSpend;
            mapForRadarData.put("value", Arrays.asList(new Double[]{costPerClick, costPerCart, costPerPurchase, incomePerPurchase, costPerMile, roi}));
            radarData.add(mapForRadarData);
        });

        Map<String, Object> result = new HashMap<>();

        result.put(group, groupNames);
        result.put("mapByIndicator", mapByIndicator);
        result.put("mapByDimension", mapByDimension);
        result.put("radarData", radarData);
        return result;
    }

    /**
     * 从Mongo中查询数据并将查询出来的id转换为名称
     *
     * @param ecReportForABTestInputParam 查询参数
     * @return 最终返回结果
     */
    private Map<String, Object> queryFromMongo(EcReportForABTestInputParam ecReportForABTestInputParam) {
        // 从Mongo中查询数据
        Map<String, Object> fbDataFromMongoForABTest = ecReportMongoService.findFbDataFromMongoForABTest(ecReportForABTestInputParam);

        // 将查询到的id转换为名称
        String group = ecReportForABTestInputParam.getGroup();
        convertResultIdToName(group, fbDataFromMongoForABTest);
        return fbDataFromMongoForABTest;
    }

    /**
     * 将查询结果中的id转换为名称
     *
     * @param group                    分组条件
     * @param fbDataFromMongoForABTest 查询结果
     */
    private void convertResultIdToName(String group, Map<String, Object> fbDataFromMongoForABTest) {
        // 转换明细中的id
        JSONArray list = JSON.parseArray(JSON.toJSONString(fbDataFromMongoForABTest.get("list")));
        JSONObject summary = JSON.parseObject(JSON.toJSONString(fbDataFromMongoForABTest.get("summary")));
        if (null == list || null == summary) {
            return;
        }
        if (null != fbDataFromMongoForABTest && ID_TO_NAME_LIST.contains(group)) {
            for (int i = 0; i < list.size(); i++) {
                JSONObject object = list.getJSONObject(i);
                String groupId = object.getString(group);
                if (null != groupId) {
                    String name = "-";   // 初始化为ID，如果没查到，默认返回"-"
                    switch (group) {
                        case "productSet":
                            JSONObject query = new JSONObject();
                            query.put("id", groupId);
                            JSONArray productSetFromDb = productSetMongoService.getProductSets(query);
                            if (!CollectionUtils.isEmpty(productSetFromDb)) {
                                name = productSetFromDb.getJSONObject(0).getString("name");
                            }
                            break;
                        case "audienceTemplate":
                            AudienceManager audienceManagerFromDb = audienceManagerRdbService.findById(NumberUtils.createLong(groupId));
                            if (null != audienceManagerFromDb) {
                                name = audienceManagerFromDb.getName();
                            }
                            break;
                        case "creativeTemplate":
                            EcTemplateMateriel ecTemplateMaterielFromDb = ecTemplateMaterielRdbService.findEcTemplateMaterielById(NumberUtils.createLong(groupId));
                            if (null != ecTemplateMaterielFromDb) {
                                name = ecTemplateMaterielFromDb.getName();
                            }
                            break;
                        case "bid":
                            if (Boolean.valueOf(groupId)) {
                                name = "Auto";
                            } else {
                                name = "Manual";
                            }
                            break;
                        default:
                            break;
                    }
                    object.put(group, name);
                }
                fbDataFromMongoForABTest.put("list", list);
            }

            // 转换统计行中的id
            JSONArray ids = summary.getJSONArray(group);
            if (null != ids) {
                Set<String> names = new HashSet<>();
                switch (group) {
                    case "productSet":
                        JSONObject query = new JSONObject();
                        JSONObject inObject = new JSONObject();
                        inObject.put("$in", ids.toArray());
                        query.put("id", inObject);
                        JSONArray productSetsFromDb = productSetMongoService.getProductSets(query);
                        if (null != productSetsFromDb) {
                            for (int i = 0; i < productSetsFromDb.size(); i++) {
                                names.add(productSetsFromDb.getJSONObject(i).getString("name"));
                            }
                        }
                        break;
                    case "audienceTemplate":
                        List<AudienceManager> audienceManagersFromDb = audienceManagerRdbService.findByIds(ids.toArray());
                        if (null != audienceManagersFromDb) {
                            audienceManagersFromDb.forEach(audienceManager -> {
                                names.add(audienceManager.getName());
                            });
                        }
                        break;
                    case "creativeTemplate":
                        List<EcTemplateMateriel> ecTemplateMaterielFromDb = ecTemplateMaterielRdbService.findByIds(ids.toArray());
                        if (null != ecTemplateMaterielFromDb) {
                            ecTemplateMaterielFromDb.forEach(ecTemplateMateriel -> {
                                names.add(ecTemplateMateriel.getName());
                            });
                        }
                        break;
                    case "bid":
                        for (int i = 0; i < ids.size(); i++) {
                            Boolean autoBid = ids.getBoolean(i);
                            if (autoBid) {
                                names.add("Auto");
                            } else {
                                names.add("Manual");
                            }
                        }
                        break;
                    default:
                        break;
                }
                summary.put(group, names);
                fbDataFromMongoForABTest.put("summary", summary);
            }
        }
    }

    /**
     * 将需要过滤的名称转换为id
     *
     * @param ecReportForABTestInputParam 查询参数
     */
    private void convertFilterNameToId(EcReportForABTestInputParam ecReportForABTestInputParam) {
        String feedId = ecReportForABTestInputParam.getFeedId();
        // 1.将需要过滤的产品系列名称转换为id
        List<String> productSets = ecReportForABTestInputParam.getProductSet();
        if (!CollectionUtils.isEmpty(productSets)) {
            JSONObject query = new JSONObject();
            JSONObject inObject = new JSONObject();
            inObject.put("$in", productSets);
            query.put("name", inObject);
            JSONArray productSetsFromDb = productSetMongoService.getProductSets(query);
            List<String> productSetId = new ArrayList<>();
            if (null != productSetsFromDb) {
                for (int i = 0; i < productSetsFromDb.size(); i++) {
                    productSetId.add(productSetsFromDb.getJSONObject(i).getString("id"));
                }
                ecReportForABTestInputParam.setProductSetId(productSetId);
            }
        }

        // 2.将需要过滤的受众定位模板名称转换为id
        List<String> audienceTemplates = ecReportForABTestInputParam.getAudienceTemplate();
        if (!CollectionUtils.isEmpty(audienceTemplates)) {
            List<AudienceManager> audienceManagersFromDb = audienceManagerRdbService.findByWeFeedIdAndNames(feedId, audienceTemplates);
            List<String> audienceTemplateId = new ArrayList<>();
            if (null != audienceManagersFromDb) {
                audienceManagersFromDb.forEach(audienceManager -> {
                    audienceTemplateId.add(audienceManager.getId().toString());
                });
                ecReportForABTestInputParam.setAudienceTemplateId(audienceTemplateId);
            }
        }

        // 3.将需要过滤的创意模板名称转换为id
        List<String> creativeTemplates = ecReportForABTestInputParam.getCreativeTemplate();
        if (!CollectionUtils.isEmpty(creativeTemplates)) {
            List<EcTemplateMateriel> ecTemplateMaterielFromDb = ecTemplateMaterielRdbService.findByWeFeedIdAndNames(feedId, creativeTemplates);
            List<String> creativeTemplateId = new ArrayList<>();
            if (null != ecTemplateMaterielFromDb) {
                ecTemplateMaterielFromDb.forEach(ecTemplateMateriel -> {
                    creativeTemplateId.add(ecTemplateMateriel.getId().toString());
                });
                ecReportForABTestInputParam.setCreativeTemplateId(creativeTemplateId);
            }
        }

        // 3.将需要过滤的出价名称转换为Boolean
        List<String> bids = ecReportForABTestInputParam.getBid();
        if (!CollectionUtils.isEmpty(bids)) {
            List<Boolean> autoBid = new ArrayList<>();
            bids.forEach(bid -> {
                if (bid.equalsIgnoreCase("auto")) {
                    autoBid.add(true);
                } else {
                    autoBid.add(false);
                }
            });
            ecReportForABTestInputParam.setAutoBid(autoBid);
        }
    }
}
